﻿using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using FFmpegVideoClip.Lib;

namespace FFmpegVideoClip.windows
{
    /// <summary>
    /// SplitFragmentWindow.xaml 的交互逻辑
    /// </summary>
    public partial class SplitFragmentWindow : Window
    {
        public enum SplitModeEnum
        {
            /// <summary>
            /// 指定时间分割
            /// </summary>
            UserTime,
            /// <summary>
            /// 固定时长
            /// </summary>
            FixedSize,
            /// <summary>
            /// 固定时长帧
            /// </summary>
            FixedFrame,
            /// <summary>
            /// 固定时长关键帧
            /// </summary>
            FixedKeyFrame,
            /// <summary>
            /// 平均分割
            /// </summary>
            AverageTime,
            /// <summary>
            /// 关键帧平均分割
            /// </summary>
            AverageKeyFrames,
            /// <summary>
            /// 精确帧平均分割
            /// </summary>
            AverageFrames,
        }
        public enum KeyFrameModeEnum { None, FrameMode, KeyFrameMode }
        public readonly record struct ResultData(SplitModeEnum SplitModeValue, string Value, TimeSpan[]? TimeSpans, KeyFrameModeEnum FramesMode);

        public static readonly DependencyProperty TimeProperty = DependencyProperty.Register("Time", typeof(string), typeof(SplitFragmentWindow));
        public string Time { get { return (string)GetValue(TimeProperty); } set { SetValue(TimeProperty, value); } }

        private TextBoxTextTimeInputChangedLimit TimeInputChangedLimit;

        public ResultData resultData;

        public SplitModeEnum SplitMode { get => (SplitModeEnum)GetValue(SplitModeProperty); set => SetValue(SplitModeProperty, value); }
        public static readonly DependencyProperty SplitModeProperty = DependencyProperty.Register("SplitMode", typeof(SplitModeEnum), typeof(SplitFragmentWindow), new PropertyMetadata(SplitModeEnum.AverageFrames));

        private Fragment? fragment;
        private bool IsInitializationCompleted = false, SpecifyMode = false;

        private CancellationTokenSource? CancelRun;

        public SplitFragmentWindow()
        {
            Owner = Application.Current.MainWindow;
            Time = TextBoxTextTimeInputChangedLimit.DefaultHistorical;
            InitializeComponent();
            IsInitializationCompleted = true;
            TimeInputChangedLimit = new();
            TimeInputChangedLimit.RegisterControl(FixedText);
            TimeInputChangedLimit.RegisterControl(TimeText);
            TimeText.Text = TextBoxTextTimeInputChangedLimit.DefaultHistorical;
            ProgressBarClear();

            TimePanel.Visibility = Visibility.Visible;
            FixedPanel.Visibility = Visibility.Collapsed;
            AveragePanel.Visibility = Visibility.Collapsed;
            SplitMode = SplitModeEnum.UserTime;
        }
        public SplitFragmentWindow(Fragment fragmentValue) : this()
        {
            fragment = fragmentValue;
        }
        public SplitFragmentWindow(SplitModeEnum splitMode, Fragment fragmentValue, bool EnableSpecifyMode = true) : this(fragmentValue)
        {
            SpecifyMode = EnableSpecifyMode;
            this.SplitMode = splitMode;
            this.Loaded += SplitFragmentWindow_Loaded;
        }

        private void SplitFragmentWindow_Loaded(object sender, RoutedEventArgs e)
        {
            SetMode();
            if (SpecifyMode)
                ModeSelectPanel.IsEnabled = false;
        }

        private async void OK_Button_Click(object sender, RoutedEventArgs e)
        {
            TimeSpan[]? timeSpans = null;
            if (SplitMode == SplitModeEnum.AverageKeyFrames || SplitMode == SplitModeEnum.AverageFrames || SplitMode == SplitModeEnum.FixedFrame || SplitMode == SplitModeEnum.FixedKeyFrame || (SplitMode == SplitModeEnum.UserTime && (FrameMode.IsChecked!.Value || KeyFrameMode.IsChecked!.Value)))
            {
                try
                {
                    if (fragment == null)
                        throw new NullReferenceException($"[{nameof(fragment)}] null，未指定实例对象");
                    int SplitCount = 0;
                    if (SplitMode != SplitModeEnum.UserTime && SplitMode != SplitModeEnum.FixedSize && SplitMode != SplitModeEnum.FixedFrame && SplitMode != SplitModeEnum.FixedKeyFrame)
                    {
                        if (!int.TryParse(AverageText.Text, out SplitCount))
                        {
                            MessageBoxEx.Show(Application.Current.MainWindow, $"只能输入数字,请重新输入");
                            return;
                        }
                        if (SplitCount < 2)
                        {
                            MessageBoxEx.Show(Application.Current.MainWindow, $"分割数不能小于2");
                            return;
                        }
                    }
                    else if (SplitMode == SplitModeEnum.UserTime && Tools.GetStringTimeToTimeSpan(TimeText.Text) < Tools.OneMillisecond)
                    {
                        MessageBoxEx.Show(Application.Current.MainWindow, $"时间错误或者小于1毫秒");
                        return;
                    }
                    else if (SplitMode == SplitModeEnum.FixedSize && Tools.GetStringTimeToTimeSpan(FixedText.Text) < Tools.OneMillisecond)
                    {
                        MessageBoxEx.Show(Application.Current.MainWindow, $"时间错误或者小于1毫秒");
                        return;
                    }
                    ProgressInit();

                    TimeSpan time = Tools.GetStringTimeToTimeSpan(TimeText.Text),
                             start = Tools.GetStringTimeToTimeSpan(fragment.StartTimeValue),
                             end = Tools.GetStringTimeToTimeSpan(fragment.EndTimeValue),
                             span = Tools.GetStringTimeToTimeSpan(FixedText.Text);
                    bool IsKeyFrameMode = KeyFrameMode.IsChecked!.Value;
                    CancelRun = new CancellationTokenSource();
                    if (SplitMode == SplitModeEnum.FixedKeyFrame)
                        timeSpans = await Task.Run(() => Tools.GetFramesTimes(fragment.InputPath,
                            Tools.GetFixedTime(start, end, span)
                            , true
                            , RangeDurationSeconds: 20
                            , MinTime: start
                            , MaxTime: end
                            , ProgressUpdate, CancelRun));
                    else if (SplitMode == SplitModeEnum.FixedFrame)

                        timeSpans = await Task.Run(() => Tools.GetFramesTimes(fragment.InputPath,
                            Tools.GetFixedTime(start, end, span)
                            , false
                            , RangeDurationSeconds: 3
                            , MinTime: start
                            , MaxTime: end
                            , ProgressUpdate, CancelRun));
                    else if (SplitMode == SplitModeEnum.AverageKeyFrames)
                        timeSpans = await Task.Run(() => Tools.GetFramesTimes(fragment.InputPath, start, end, SplitCount, true, ProgressUpdate, CancelRun));
                    else if (SplitMode == SplitModeEnum.AverageFrames)
                        timeSpans = await Task.Run(() => Tools.GetFramesTimes(fragment.InputPath, start, end, SplitCount, false, ProgressUpdate, CancelRun));
                    else //指定时间分割
                    {
                        CurrentProgressBar.Maximum = end.TotalSeconds;
                        

                        timeSpans = await Task.Run(() => Tools.GetFramesTimes(fragment.InputPath, time, IsKeyFrameMode,
                            RangeDurationSeconds: IsKeyFrameMode ? 20 : 3,
                            MinTime: start,
                            MaxTime: end,
                            CurrentTimeSpan: ProgressUpdate,
                            cancellationTokenSource: CancelRun));
                    }
                    if (CancelRun.IsCancellationRequested)
                    {
                        ProgressBarClear();
                        CancelRun = null;
                        return;
                    }
                    CancelRun = null;
                    if ((timeSpans == null) || timeSpans.Length < SplitCount - 1)
                        throw new FileLoadException($"视频文件:{Path.GetFileName(fragment.InputPath)} 未读到有效关键帧或者关键帧数量少于分割数量[{SplitCount}]个");
                }
                catch (Exception exc)
                {
                    LogManage.WriteExceptionInfo($"获取关键帧错误:{Path.GetFileName(fragment?.InputPath)}", exc, true);
                    return;
                }
            }

            resultData = new(SplitMode,
                SplitMode == SplitModeEnum.UserTime ? TimeText.Text :
                SplitMode == SplitModeEnum.FixedSize || SplitMode == SplitModeEnum.FixedFrame || SplitMode == SplitModeEnum.FixedKeyFrame ? FixedText.Text : AverageText.Text
                , timeSpans,
                SplitMode == SplitModeEnum.AverageKeyFrames || SplitMode == SplitModeEnum.FixedKeyFrame || KeyFrameMode.IsChecked!.Value ? KeyFrameModeEnum.KeyFrameMode
                : SplitMode == SplitModeEnum.FixedFrame || SplitMode == SplitModeEnum.AverageFrames || FrameMode.IsChecked!.Value ? KeyFrameModeEnum.FrameMode : KeyFrameModeEnum.None);

            DialogResult = true;
        }
        private void Cancel_Button_Click(object sender, RoutedEventArgs e) { if (CancelRun != null) { CancelRun.Cancel(); return; } DialogResult = false; }
        private void ModeSelect_RadioButton_Checked(object sender, RoutedEventArgs e)
        {
            if (!IsInitializationCompleted || sender is not RadioButton button)
                return;
            string mode = button.Tag?.ToString() ?? string.Empty;
            SplitMode = mode switch
            {
                "Fixed" => SplitModeEnum.FixedSize,
                "FixedKeyFrame" => SplitModeEnum.FixedKeyFrame,
                "FixedFrame" => SplitModeEnum.FixedFrame,
                "AverageFrames" => SplitModeEnum.AverageFrames,
                "Average" => SplitModeEnum.AverageTime,
                "AverageKeyFrames" => SplitModeEnum.AverageKeyFrames,
                _ => SplitModeEnum.UserTime
            };
            SetMode();
        }
        private void SetMode()
        {
            switch (SplitMode)
            {
                case SplitModeEnum.FixedSize:
                case SplitModeEnum.FixedKeyFrame:
                case SplitModeEnum.FixedFrame:
                    TimePanel.Visibility = Visibility.Collapsed;
                    FixedPanel.Visibility = Visibility.Visible;
                    AveragePanel.Visibility = Visibility.Collapsed;
                    return;
                case SplitModeEnum.AverageFrames:
                case SplitModeEnum.AverageTime:
                case SplitModeEnum.AverageKeyFrames:
                    TimePanel.Visibility = Visibility.Collapsed;
                    FixedPanel.Visibility = Visibility.Collapsed;
                    AveragePanel.Visibility = Visibility.Visible;
                    return;
                default:
                    TimePanel.Visibility = Visibility.Visible;
                    FixedPanel.Visibility = Visibility.Collapsed;
                    AveragePanel.Visibility = Visibility.Collapsed;
                    return;
            }
        }

        private void ProgressInit()
        {
            //if (fragment != null)
            //    CurrentProgressBar.Maximum = GetMediaInfo.GetInfo(fragment.InputPath).Duration.TotalMilliseconds;
            //else
            //    CurrentProgressBar.Maximum = 100;
            CurrentProgressBar.Maximum = Tools.GetStringTimeToTimeSpan(fragment?.EndTimeValue ?? "01:40.0").TotalSeconds;
            CurrentProgressBar.Value = 0;
        }
        private void ProgressUpdate(TimeSpan Value)
        {
            this.Dispatcher.BeginInvoke((Action)delegate 
            {
                CurrentProgressBar.Value = Value.TotalSeconds;
                CurrentProgressBarText.Text = $"{Tools.GetTimeSpanToString_Digit(Value)}/{Tools.GetTimeSpanToString_Digit(TimeSpan.FromSeconds(CurrentProgressBar.Maximum))}";
            });
            
        }

        private void ProgressBarClear()
        {
            CurrentProgressBar.Value = 0;
            CurrentProgressBarText.Text = string.Empty;
        }

        private void GetCurrentTimeButton_Click(object sender, RoutedEventArgs e)
        {
            if (!(MainWindow.Instance?.Mec.IsOpen ?? false))
            {
                MainWindow.ShowStatusInfo("获取当前时间失败，没有正在打开中的媒体文件");
                return;
            }
            TimeText.Text = Tools.GetTimeSpanToString(MainWindow.Instance.Mec.CurrentTimePosition);
        }
    }
}
